package com.wang.tool.extension;

import com.wang.tool.common.SimpleException;
import com.wang.tool.monitor.InvokeTemplate;
import com.wang.tool.monitor.template.AbsMonitorInvoker;
import com.wang.tool.util.SimpleLogUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 扩展点工厂
 *
 * @Author wangshaopeng
 * @Date 2022/5/14
 */
public class ExtensionFactory {

    /**
     * 默认名称
     * 仅是个常量,无实际语义
     */
    public static final String DEF_NAME = "DEF";

    /**
     * 扩展点配置
     */
    private final Map<Class<?>, ExtensionAttr> attrs = new HashMap<>();

    /**
     * 扩展点实现
     */
    private final Map<String, Object> extensions = new HashMap<>();


    /**
     * 取扩展点代理对象
     *
     * @param extensionType 接口类
     * @param <T>
     * @return
     */
    public <T> T getExtensionProxy(Class<T> extensionType) {
        ExtensionAttr attr = attrs.get(extensionType);
        return attr == null ? null : (T) attr.getProxy();
    }


    /**
     * 有没有该扩展点实现
     *
     * @param extensionType 接口类
     * @param name          实现名称
     * @param <T>
     * @return
     */
    public <T> T getExtensionImpl(Class<T> extensionType, String name) {
        if (name == null) {
            return null;
        }
        return (T) extensions.get(buildExtensionId(extensionType, name));
    }

    /**
     * 有没有扩展点
     *
     * @param extensionType 接口类
     * @return
     */
    public boolean hasExtension(Class<?> extensionType) {
        return attrs.get(extensionType) != null;
    }

    /**
     * 有没有该扩展点实现
     *
     * @param extensionType 接口类
     * @param name          实现名称
     * @return
     */
    public boolean hasExtensionImpl(Class<?> extensionType, String name) {
        if (name == null) {
            return false;
        }
        return extensions.get(buildExtensionId(extensionType, name)) != null;
    }

    /**
     * 发布扩展点
     *
     * @param extensionType 接口类 非空
     * @param selector      选择器 非空
     * @return
     */
    public synchronized void publishExtension(Class<?> extensionType, ExtensionSelector selector) {
        //基础非空
        if (extensionType == null || !extensionType.isInterface() || selector == null) {
            throw new IllegalArgumentException("extensionType/selector can not be null");
        }
        //重复
        if (hasExtension(extensionType)) {
            throw new IllegalArgumentException("extensionType:" + extensionType.getName() + " is already declared");
        }
        ExtensionAttr attr = new ExtensionAttr();
        attr.setExtensionType(extensionType);
        attr.setSelector(selector);
        attr.setProxy(Proxy.newProxyInstance(extensionType.getClassLoader(), new Class[]{extensionType}, new ExtensionMethodHandler(attr)));
        attrs.put(extensionType, attr);
        SimpleLogUtil.info("[publishExtension] extensionType:{0}, selector:{1}", extensionType.getName(), selector);
    }


    /**
     * 安装扩展点实现
     *
     * @param extensionType 接口类 非空
     * @param name          实现名称 非空
     * @param extensionImpl 接口实现 非空
     * @param <T>
     * @return
     */
    public synchronized <T> void installExtensionImpl(Class<T> extensionType, String name, T extensionImpl) {
        //基础非空
        if (extensionType == null || name == null || extensionImpl == null) {
            throw new IllegalArgumentException("extensionType/name/extensionImpl can not be null");
        }
        //接口实现
        if (!extensionType.isAssignableFrom(extensionImpl.getClass())) {
            throw new IllegalArgumentException("extensionImpl:" + extensionImpl + " must implements extensionType:" + extensionType.getName());
        }
        //重复
        if (hasExtensionImpl(extensionType, name)) {
            throw new IllegalArgumentException("extensionType:" + extensionType.getName() + " already has name:" + name + "'s implements:" + getExtensionImpl(extensionType, name));
        }
        extensions.put(buildExtensionId(extensionType, name), extensionImpl);
        SimpleLogUtil.info("[installExtensionImpl] extensionType:{0}, name:{1}, extensionImpl:{2}", extensionType, name, extensionImpl);
    }

    /**
     * 卸载扩展点实现
     *
     * @param extensionType 接口类 非空
     * @param name          实现名称 非空
     * @return
     */
    public synchronized void uninstallExtensionImpl(Class<?> extensionType, String name) {
        if (name == null) {
            return;
        }
        extensions.remove(buildExtensionId(extensionType, name));
        SimpleLogUtil.info("[uninstallExtensionImpl] extensionType:{0}, name:{1}", extensionType, name);
    }


    /**
     * 拼接扩展点key
     *
     * @param extensionType 接口类
     * @param name          实现名称
     * @return
     */
    private String buildExtensionId(Class<?> extensionType, String name) {
        return extensionType.getName() + "_" + name;
    }

    /**
     * 扩展点属性
     */
    public static class ExtensionAttr {

        /**
         * 接口类
         */
        private Class<?> extensionType;

        /**
         * 选择器
         */
        private ExtensionSelector selector;

        /**
         * 代理类
         */
        private Object proxy;


        public Class<?> getExtensionType() {
            return extensionType;
        }

        public void setExtensionType(Class<?> extensionType) {
            this.extensionType = extensionType;
        }

        public ExtensionSelector getSelector() {
            return selector;
        }

        public void setSelector(ExtensionSelector selector) {
            this.selector = selector;
        }

        public Object getProxy() {
            return proxy;
        }

        public void setProxy(Object proxy) {
            this.proxy = proxy;
        }

        @Override
        public String toString() {
            return "ExtensionAttr{" + "extensionType=" + extensionType + ", selector=" + selector + ", proxy=" + proxy + '}';
        }
    }

    /**
     * 扩展点函数转发
     */
    private class ExtensionMethodHandler implements InvocationHandler {

        private final Class<?> extensionType;
        private final String extensionName;
        private final ExtensionSelector selector;
        private final ExtensionLibrary extensionLibrary;
        private final Map<Method, Integer> sceneIndexMap = new ConcurrentHashMap<>();


        public ExtensionMethodHandler(ExtensionAttr attr) {
            this.extensionType = attr.getExtensionType();
            this.extensionName = extensionType.getSimpleName();
            this.selector = attr.getSelector();
            this.extensionLibrary = implName -> {
                if (implName == null) {
                    return null;
                }
                return extensions.get(buildExtensionId(extensionType, implName));
            };
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //非接口定义函数
            if (!extensionType.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            return InvokeTemplate.monitor("Extension", extensionName, method.getName(), new AbsMonitorInvoker<Object>(args) {

                @Override
                protected boolean isSpecialPrintResult() {
                    return true;
                }

                @Override
                public Object internalCall() throws Throwable {
                    Integer sceneIndex = sceneIndexMap.computeIfAbsent(method, method1 -> {
                        Class<?>[] parameterTypes = method1.getParameterTypes();
                        for (int i = 0; i < parameterTypes.length; i++) {
                            Class<?> parameterType = parameterTypes[i];
                            if (ExtensionScene.class.isAssignableFrom(parameterType)) {
                                return i;
                            }
                        }
                        return null;
                    });
                    if (sceneIndex == null) {
                        throw new SimpleException(SimpleException.UN_SUPPORT, "method:{0} dont find param instance ExtensionScene", method.toGenericString());
                    }

                    ExtensionScene scene = (ExtensionScene) args[sceneIndex];
                    Object extensionImpl = selector.selectImpl(extensionType, scene, extensionLibrary);
                    if (extensionImpl == null) {
                        throw new SimpleException(SimpleException.UN_EXPECT, "extensionType:{0},scene:{1} not adapter impl", method.toGenericString(),scene);
                    }
                    return method.invoke(extensionImpl, args);
//                    Class<?> returnType = method.getReturnType();
//                    if (!returnType.isPrimitive() || returnType.equals(void.class)) {
//                        return null;
//                    }
//                    if (returnType.equals(int.class)) {
//                        return 0;
//                    }
//                    if (returnType.equals(boolean.class)) {
//                        return false;
//                    }
//                    if (returnType.equals(double.class)) {
//                        return 0.0D;
//                    }
//                    if (returnType.equals(long.class)) {
//                        return 0L;
//                    }
//                    if (returnType.equals(byte.class)) {
//                        return 0;
//                    }
//                    if (returnType.equals(short.class)) {
//                        return 0;
//                    }
//                    if (returnType.equals(float.class)) {
//                        return 0.0F;
//                    }
//                    if (returnType.equals(char.class)) {
//                        return '\u0000';
//                    }
//                    return null;
                }

            });
        }
    }


    @FunctionalInterface
    public interface ExtensionLibrary {

        /**
         * 从仓库获取扩展实现
         *
         * @param implName 实现名
         * @return 适配的实现
         */
        Object getImpl(String implName);
    }

}
